home *** CD-ROM | disk | FTP | other *** search
/ Aminet 12 / Aminet 12 (1996)(GTI - Schatztruhe)[!][Jun 1996].iso / Aminet / dev / lang / HeliOS4.lha / helios_demo_disk4 / tutorials / HeliOSLanguageTutorial4.doc < prev    next >
Encoding:
Text File  |  1995-04-21  |  82.4 KB  |  2,733 lines

  1.  
  2. Topics covered in this tutorial:
  3.  
  4. * About this tutorial...
  5. * Running examples
  6. * Decimal number entry
  7. * Numbers and "literals"
  8. * Moving between number sizes
  9. * Useful mixed number size operators
  10. * Useful compound arithmetic operators
  11. * Defining 32-bit (or "double") numbers for numeric input
  12. * The order of bytes in memory
  13. * Bit numbering conventions
  14. * Logical AND, OR and XOR operations
  15. * Signed and unsigned numbers
  16. * Displaying signed and unsigned numbers
  17. * Comparing signed and unsigned numbers
  18. * Logical and Arithmetic number shifting
  19. * Numeric bases
  20. * The useful numeric base control functions: #B, #D, and #H
  21. * ASCII characters
  22. * Number formatting
  23. * Using scaled and fractional integer arithmetic instead of floating point
  24. * Examples and problems
  25.  
  26.  
  27. ----------------------
  28. About this tutorial...
  29. ----------------------
  30.  
  31. This tutorial is the most advanced so far, and is intended to give you
  32. some important background to general methods of computing using HeliOS,
  33. with particular emphasis on "numbers".
  34.  
  35. Again the subject matter is miscellaneous, and you may pick and choose what
  36. you want to read: however, we do strongly recommend that you become familiar
  37. with EVERYTHING that is presented here, since all of it is important for
  38. general HeliOS programming.
  39.  
  40. At the end of this tutorial are a few example problems for you to try.
  41.  
  42.  
  43. ----------------
  44. Running examples
  45. ----------------
  46.  
  47. This tutorial contains many shorts sections of example code.
  48.  
  49. Remember that you can easily run these examples from the editor by simply
  50. highlighting them and pressing Left-Amiga-e.
  51.  
  52. Note that many short example phrases have associated comments to the right
  53. of them, often using a preceding "->" symbol.
  54.  
  55. These "->" symbols, and the following comments, are merely intended to
  56. indicate what the code fragment is doing, and should not be included in
  57. highlighted sections of code to be run-tested: if you do try to run these
  58. comments, you will merely get an error.
  59.  
  60.  
  61. --------------------
  62. Decimal number entry
  63. --------------------
  64.  
  65. You have seen already in the previous tutorials that double length (32-bit)
  66. numbers are specified by including a "." character within the number as it
  67. is entered.
  68.  
  69. As we said earlier, this is not specifically meaningful in terms of being a
  70. decimal point: all the "." character does is tell HeliOS to interpret the
  71. number as 32-bit.
  72.  
  73. However, there is a little trick whereby HeliOS can actually use the "."
  74. character to indicate a decimal point in number entry, because HeliOS does
  75. actually store the offset position of this "." in a special system variable
  76. called DPL.  This simple mechanism is included to allow you to interpret
  77. numbers as decimals within your program if you wish.
  78.  
  79. Actually, if a "." character is encountered in a numeric entry, the DPL
  80. variable will contain the number of characters AFTER the "decimal point".
  81.  
  82. Thus:
  83.  
  84. 123.00   DPL @L .  -> Prints "2"
  85. 1.2300   DPL @L .  -> Prints "4"
  86.   ^^^^
  87.   DPL stores this character count (= number of characters after ".")
  88.  
  89. If you look in DPL after entering a 16-bit number you will always find the
  90. value "-1".
  91.  
  92. Thus:
  93.  
  94. 12300    DPL @L .  -> Prints "-1"
  95.  
  96. Having registered the decimal point position using DPL, it is a simple
  97. matter for your program to store this information and use it to deal with
  98. the number as a "decimal" if required.
  99.  
  100. Note that you MUST READ "DPL" IMMEDIATELY after a number has been entered,
  101. otherwise it may no longer contain a value relevant to that number.
  102.  
  103. See later in this tutorial for more details on how you might handle decimal
  104. numbers using integer arithmetic.
  105.  
  106.  
  107. ----------------------
  108. Numbers and "literals"
  109. ----------------------
  110.  
  111. You will see the term "literal" used in HeliOS (and FORTH) documentation,
  112. and it is useful to know a little more about what this means.
  113.  
  114. The term "literal" is used loosely in FORTH to specify that a number (or
  115. even sometimes a text string) should be evaluated and (possibly) compiled
  116. into the dictionary rather than used immediately.
  117.  
  118. Look at the definition of the HeliOS word "LITERAL":
  119.  
  120. LITERAL      ( n1 _ _ _ )
  121.  
  122.              Used within colon definitions to compile the CFA of the word
  123.              "LIT" and then compile n1 as 16-bit literal into dictionary.
  124.  
  125.              At run time n1 will be pushed onto stack.
  126.  
  127.              This is a state-sensitive word which does nothing if not
  128.              used within a colon definition.
  129.  
  130. This word LITERAL is a state-sensitive word, and thus has different actions
  131. depending upon whether the system is in compile-mode or execute-mode (i.e.
  132. whether the system is compiling a colon definition or executing directly).
  133.  
  134. If the word LITERAL is executed in direct execution mode it will just do
  135. nothing at all.
  136.  
  137. If the word LITERAL is executed while a colon definition is being compiled
  138. (within the definition) it will cause the current 16-bit top-of-stack number
  139. to be compiled into the body of the word being defined, along with special
  140. code which will place that number on the stack when the word is run later.
  141.  
  142. This is achieved by compiling the CFA of a special word LIT, followed by
  143. the 16-bit numeric value.  At run time, the action of LIT will be to put
  144. the following number onto the stack and advance the program pointer to the
  145. next position after the number storage space.
  146.  
  147. When an apparently "ordinary number" is interpreted by HeliOS, either in
  148. the command line or a program, HeliOS always first has to read the actual
  149. numeric expression in the text and "interpret" the number.
  150.  
  151. If the number is a legal and valid expresion, the process of interpreting
  152. it will leave the number on the HeliOS stack.
  153.  
  154. HeliOS then has to do something with the number, and in this case it needs
  155. to decide whether to simply carry on, with the number on the stack, or
  156. compile the number into the current word definition for use later.
  157.  
  158. HeliOS checks to see if it is currently within the process of interpreting
  159. a colon definition (i.e. if it is currently compiling a word) and if so
  160. it will compile the number as a "literal".  If it is not within a colon
  161. definition HeliOS will simply carry on with the number on the stack.
  162.  
  163.  
  164. ---------------------------
  165. Moving between number sizes
  166. ---------------------------
  167.  
  168. Often you need to change from a 16-bit to a 32-bit number or address, and
  169. it is useful to be able to do this fluently and quickly.
  170.  
  171. Addresses are very easy to convert:
  172.  
  173. 16-bit to 32-bit ->     Use W>L
  174.  
  175. 32-bit to 16-bit ->     Use L>W
  176.  
  177. Signed numbers are converted as follows:
  178.  
  179. 16-bit to 32-bit ->     Use S->D
  180.  
  181.                         or...
  182.  
  183.                         If the number to convert is known to be positive,
  184.                         you can just place an extra 16-bit "0" cell on
  185.                         the stack, as with unsigned numbers.
  186.  
  187.                         e.g.
  188.  
  189.                         7   = 16-bit value 7
  190.  
  191.                         7 0 = 32-bit value 7
  192.  
  193.                         or...
  194.  
  195.                         If the number to convert is known to be negative,
  196.                         you can just place an extra 16-bit "-1" cell on
  197.                         the stack.
  198.  
  199.                         e.g.
  200.  
  201.                         -7    = 16-bit value 7
  202.  
  203.                         -7 -1 = 32-bit value 7
  204.  
  205.  
  206. 32-bit to 16-bit ->     Use DROP, but note that if the number exceeds
  207.                         16-bit limits, the result will be truncated.
  208.  
  209. Unsigned numbers are converted as follows:
  210.  
  211. 16-bit to 32-bit ->     Place an extra 16-bit "0" cell on the stack.
  212.  
  213.                         e.g.
  214.  
  215.                         7   = 16-bit value 7
  216.  
  217.                         7 0 = 32-bit value 7
  218.  
  219. 32-bit to 16-bit ->     Use  DROP, but note that if the number exceeds
  220.                         16-bit limits, the result will be truncated.
  221.  
  222.  
  223. ----------------------------------
  224. Useful mixed number size operators
  225. ----------------------------------
  226.  
  227. Often it is useful to be able to carry out arithmetic operations on 16-bit
  228. and 32-bit numbers without having to convert the numbers to the same size
  229. first.
  230.  
  231. HeliOS has several mixed size operators for commonly required functions:
  232.  
  233.      M*            ( n1 n2 _ _ _ d1 )
  234.  
  235.                    Returns the double number signed product of n1 * n2 = d1.
  236.  
  237.      M*/           ( d1 n1 n2 _ _ _ d2 )
  238.  
  239.                    Multiplies d1 by n1 and divides by n2 returning quotient
  240.                    d2.
  241.  
  242.      M+            ( d1 n1 _ _ _ d2 )
  243.  
  244.                    Returns the double number sum of a single and a double
  245.                    number.
  246.  
  247.      M-            ( d1 n1 _ _ _ d2 )
  248.  
  249.                    Returns the double number difference between a single and
  250.                    a double number.
  251.  
  252.      M/            ( d1 n1 _ _ _ n2 )
  253.  
  254.                    Returns the signed single number quotient of the mixed
  255.                    magnitude division of d1 by n1.
  256.  
  257.      M/MOD         ( d1 n1 _ _ _ n2 n3 )
  258.  
  259.                    Returns the signed remainder and quotient, n2 and n3, of
  260.                    the division d1 / n1.
  261.  
  262.                    The remainder n2 takes its sign from d1.
  263.  
  264.  
  265. ------------------------------------
  266. Useful compound arithmetic operators
  267. ------------------------------------
  268.  
  269. Some of the most often used arithmetic functions have special compound
  270. operators which improve compilation and execution speed as well as being
  271. easier to type into source code: they also take up less memory space.
  272.  
  273. For example, to add 1 to a number on the stack you can use the following
  274. long or short methods:
  275.  
  276. 1 +            -> Two words
  277.  
  278. or
  279.  
  280. 1+             -> Single word is quicker and easier
  281.  
  282. There are a number of these special compound operators, and they are well
  283. worth learning, so we include below a brief listing.
  284.  
  285. Single length:
  286.  
  287.      1+            ( n1 _ _ _ n2 )
  288.  
  289.                    Returns n1 incremented by 1 as n2.
  290.  
  291.      1-            ( n1 _ _ _ n2 )
  292.  
  293.                    Returns n1 decremented by 1 as n2.
  294.  
  295.      2*            ( n1 _ _ _ n2 )
  296.  
  297.                    Returns n1 * 2 as n2.
  298.  
  299.      4*            ( n1 _ _ _ n2 )
  300.  
  301.                    Returns n1 * 4 as n2.
  302.  
  303.      2+            ( n1 _ _ _ n2 )
  304.  
  305.                    Returns n1 incremented by 2 as n2.
  306.  
  307.      2-            ( n1 _ _ _ n2 )
  308.  
  309.                    Returns n1 decremented by 2 as n2.
  310.  
  311.      2/            ( n1 _ _ _ n2 )
  312.  
  313.                    Returns n1 / 2 as n2.
  314.  
  315.      4/            ( n1 _ _ _ n2 )
  316.  
  317.                    Returns n1 / 4 as n2.
  318.  
  319. Double length:
  320.  
  321.  
  322.      D2*           ( d1 _ _ _ d2 )
  323.  
  324.                    Returns d1 * 2 as d2.
  325.  
  326.      D4*           ( d1 _ _ _ d2 )
  327.  
  328.                    Returns d1 * 4 as d2.
  329.  
  330.      D1+           ( d1 _ _ _ d2 )
  331.  
  332.                    Returns d1 incremented by 1 as d2.
  333.  
  334.      D1-           ( d1 _ _ _ d2 )
  335.  
  336.                    Returns d1 decremented by 1 as d2.
  337.  
  338.      D2+           ( d1 _ _ _ d2 )
  339.  
  340.                    Returns d1 incremented by 2 as d2.
  341.  
  342.      D2-           ( d1 _ _ _ d2 )
  343.  
  344.                    Returns d1 decremented by 2 as d2.
  345.  
  346.      D2/           ( d1 _ _ _ d2 )
  347.  
  348.                    Returns d1 / 2 as d2.
  349.  
  350.      D4/           ( d1 _ _ _ d2 )
  351.  
  352.                    Returns d1 / 4 as d2.
  353.  
  354.  
  355. See the "Dictionary.doc" also for details of some useful and time saving
  356. special variable and memory "storage" words such as +!, 1!, CYCLE+! etc.
  357.  
  358.  
  359. -------------------------------------------------------
  360. Defining 32-bit (or "double") numbers for numeric input
  361. -------------------------------------------------------
  362.  
  363. You have already learned that to specify a number as "double", or 32-bit,
  364. you must include a "." anywhere within the input number representation.
  365.  
  366. For example:
  367.  
  368. 1     -> 16-bit number 1
  369. 1.    -> 32-bit number 1
  370. 1000  -> 16-bit number 1000
  371. 1.000 -> 32-bit number 1000
  372. 10.00 -> 32-bit number 1000
  373. 100.0 -> 32-bit number 1000
  374. 1000. -> 32-bit number 1000
  375.     ^
  376.     A "." character in any position specifies that a number is 32-bit
  377.  
  378. This convention of using "." as a 32-bit number specifier was part of the
  379. traditional FORTH language, and has been retained by HeliOS.
  380.  
  381. However, HeliOS includes two extra choices of "specifier character" when
  382. you want to input a 32-bit number.
  383.  
  384. These are the characters "-" and ",", so in HeliOS we can have:
  385.  
  386. 100.0 -> 32-bit number 1000    ->  Using a "point" specifier
  387. 1,000 -> 32-bit number 1000    ->  Using a "comma" specifier
  388. 10-00 -> 32-bit number 1000    ->  Using a "Hyphen" specifier
  389.  
  390. Note that the position of any of these specifiers, if present, will be
  391. recorded in DPL.
  392.  
  393. Another interesting point about this is that in traditional FORTH you were
  394. only allowed to have one instance of the double number specifier within any
  395. one number string.
  396.  
  397. Thus, something like this would produce an error in traditional FORTH:
  398.  
  399. 200.00.00
  400.  
  401. In HeliOS, however, you can have multiple specifiers, so that all of the
  402. following expressions are legal:
  403.  
  404. 2,000,000.00
  405.  
  406. 20-00-00
  407.  
  408. 20.00-00-1,00
  409.  
  410. If several of the above characters are encountered in the numeric expression
  411. only the position of the last, or "rightmost", one will be stored in DPL.
  412.  
  413. Please read the Dictionary.doc section on "GENERAL NOTATION" for more
  414. details on numeric entry.
  415.  
  416.  
  417. ----------------------------
  418. The order of bytes in memory
  419. ----------------------------
  420.  
  421. It is very important to know that Motorola 680xx CPUs, by convention, store
  422. multiple byte quantities in the following order:
  423.  
  424. High order byte  -> stored in lowest address
  425. Low order byte   -> stored in highest address
  426.  
  427. This is not as silly as it seems, because you can easily read numbers in
  428. hex format stored in this way, especially when they are printed out in a
  429. standard memory dump like this:
  430.  
  431.  ADDRESS  D8 D9 DA DB DC DD DE DF  E0 E1 E2 E3 E4 E5 E6 E7  89ABCDEF01234567
  432.  
  433.   7A5A560 00 AE BC 7C 06 F8 67 00  00 A6 BC 7C 11 7E 67 00  ...|..g....|.~g.
  434.           ^^       ^^
  435.           ||       ||
  436.       High byte  Low byte     ------------------> High memory
  437.           |||||||||||
  438.           ^^^^^^^^^^^
  439.            Longword
  440.  
  441. It is important always to be aware of this storage convention, especially
  442. when dealing with numbers on the HeliOS stack.
  443.  
  444. A "double" 32-bit number on the HeliOS stack always has its low word in the
  445. higher memory address, and its high word in the lower memory address.
  446.  
  447. However, there is a further twist to this story.....
  448.  
  449. The HeliOS system and Return stacks are filled from high to low memory, so
  450. the TOP of the stack has the lowest memory address!
  451.  
  452. The stack grows downwards in terms of memory address, and the stack pointer
  453. is decremented with each new item added to the stack.
  454.  
  455. This means, in conjunction with the previously mentioned Motorola storage
  456. convention, that the high order word of any double number on the stack is
  457. always nearer to the top of the stack (the last item added) than the low
  458. order word.
  459.  
  460.  
  461. Look at this example:
  462.  
  463.  
  464.                        Low Memory/Top of stack
  465.  
  466.                                   |
  467.  
  468. 123 = Top of stack item 16-bit number 123 = Last item added to stack
  469.  
  470. 456 = 2nd on stack item 16-bit number 456 = Next-to-last item added to stack
  471.  
  472. 0   = High word of 3rd on stack item 32-bit number 789. }  Two stack cells
  473.                                                         }- holding 32-bit
  474. 789 = Low  word of 3rd on stack item 32-bit number 789. }  number value
  475.  
  476.                                   |
  477.  
  478.                      High Memory/Bottom of stack
  479.  
  480.  
  481. We put the top of the HeliOS stack uppermost in the above illustration
  482. because we conventionally talk of the "Top" of the stack.
  483.  
  484. However, if we wanted to display the progression from high to low memory,
  485. we might write:
  486.  
  487.                      High Memory/Bottom of stack
  488.  
  489.                                   |
  490.  
  491. 789    <- Low  word of 3rd on stack 32-bit number 789.
  492. 0      <- High word of 3rd on stack 32-bit number 789.
  493. 456    <- 2nd on stack 16-bit number 456
  494. 123    <- Top of stack 16-bit number 123
  495.  
  496.                                   |
  497.  
  498.                        Low Memory/Top of stack
  499.  
  500. Try experimenting with the stack in the interpreter until you are VERY
  501. familiar with all this, because there is no doubt that you will make many
  502. mistakes over stack handling if you do not have an absolutely clear idea
  503. of what is going on.
  504.  
  505.  
  506. -------------------------
  507. Bit numbering conventions
  508. -------------------------
  509.  
  510. All numbers are stored in the computer as binary bit sequences, and there
  511. is a convention which specifies these bits in terms of a "bit number".
  512.  
  513. Bit-positions within any number are generally specified in a sequence from
  514. right to left, and are numbered from 0 on the right.
  515.  
  516. The rightmost bit is called the "least significant bit", or "LSB".
  517.  
  518. The leftmost bit is called the "most significant bit", or "MSB".
  519.  
  520. (The leftmost bit is also sometimes called the "sign bit". See later.)
  521.  
  522. For example:
  523.  
  524. An 8-bit number -> 11111111
  525.                    ^      ^
  526.                   MSB    LSB
  527.                    7      0
  528.  
  529. A 16-bit number -> 1111111111111111
  530.                    ^              ^
  531.                   MSB            LSB
  532.                   15              0
  533.  
  534.  
  535. ----------------------------------
  536. Logical AND, OR and XOR operations
  537. ----------------------------------
  538.  
  539. The HeliOS words AND, OR, and XOR use binary bit logic to combine two
  540. numbers.
  541.  
  542. As you may know, in these operations each bit is treated independently, on
  543. a columnar basis, and there are no carries from one bit-column to the next.
  544.  
  545. Let us first examine the AND operation.
  546.  
  547. In an AND operation, for any result-bit to be "1", the corresponding bits
  548. in the two source numbers must BOTH be "1".
  549.  
  550. Let us see what happens when we AND two binary numbers:
  551.  
  552.         0000000011111111
  553.         0110010110100010  AND
  554.         ----------------
  555.         0000000010100010
  556.  
  557. Notice in this example that the top number contains all "0"s in the high
  558. byte and all "1"s in the low byte.  The effect of this on the second number
  559. in this example is that the pattern of the low-order eight bits is retained,
  560. but the high-order eight bits are all set to zero.
  561.  
  562. Here the top number is being used as a "mask" to mask out the high-order
  563. byte of the second number.
  564.  
  565.  
  566. Let us now examine the OR operation.
  567.  
  568. In an OR operation, a "1" in the same column of EITHER number produces a
  569. "1" in the result.
  570.  
  571. Let us see what happens when we OR two binary numbers:
  572.  
  573.         1000100100001001
  574.         0000001111001000  OR
  575.         ----------------
  576.         1000101111001001
  577.  
  578. Once again, note that each column is treated separately with no carries,
  579. and in this case the "1"s in both numbers have been "logically" combined.
  580.  
  581.  
  582. Logical operations can be used to test individual bits of a number, and
  583. this can be useful for many things, such as reading hardware registers or
  584. testing bits in a flag word.
  585.  
  586. For example, you might have a 16-bit flag word, and you might want to test
  587. the current state of bit 3.
  588.  
  589. Here is how you could do it.
  590.  
  591.         1011101010011100  <- The source flag word we wish to test
  592.                     ^
  593.              We want to test bit 3
  594.  
  595. So we can use a mask with only this bit set:
  596.  
  597.         0000000000001000  <- Mask
  598.                     ^
  599.          Only the bit we want to test is set
  600.  
  601. Then we use an AND operation:
  602.  
  603.         1011101010011100
  604.         0000000000001000   AND
  605.         ----------------
  606.         0000000000001000
  607.  
  608. Since the bit was "1", the result is non-zero, which can be used as a
  609. "true" value representing the state of the tested bit: had it been "0"
  610. the result would have been "0" or "false".
  611.  
  612. We can use the OR operator, if we wish, to set a bit of our flag word.
  613.  
  614. Let us assume we want to set bit 1 of the flag word:
  615.  
  616. First we make a mask with only bit 1 set:
  617.  
  618.         0000000000000010
  619.                       ^
  620.                Only bit 1 set
  621.  
  622. Then we use the OR operation:
  623.  
  624.         1011101010011100
  625.         0000000000000010   OR
  626.         ----------------
  627.         1011101010011110
  628.                       ^
  629.                Bit 1 is now set
  630.  
  631. What if we wanted to clear bit 2 of our flag word without changing any of
  632. the other bits?
  633.  
  634. In this case we would make a mask like this with only bit 2 clear:
  635.  
  636.         1111111111111011
  637.                      ^
  638.             Only bit 2 is clear
  639.  
  640. Now we could use the AND operation again:
  641.  
  642.         1011101010011100
  643.         1111111111111011  AND
  644.         ----------------
  645.         1011101010011000
  646.                      ^
  647.             Bit 2 is now clear
  648.  
  649. Notice that we used an inverse mask that contains all "1"s except for the
  650. bit which we wanted to set to "0".
  651.  
  652.  
  653. Another useful logical bit operator is XOR.
  654.  
  655. The XOR operator tests corresponding bits and only generates a "1" in the
  656. result when both "source" bits are different.
  657.  
  658. This has an interesting and useful effect.
  659.  
  660. Here is an example, using a mask with all bits set to "1":
  661.  
  662.         1011101010011100
  663.         1111111111111111  XOR
  664.         ----------------
  665.         0100010101100011
  666.  
  667. Notice that this operation has simply flipped all the bits of the top value.
  668.  
  669. This is a very common use of XOR, and is well worth remembering:
  670.  
  671. * Performing an XOR with a mask of all "1"s will flip all the bits of the
  672.   source value.
  673.  
  674. Look at another example, with a mask of all "0"s:
  675.  
  676.         1011101010011100
  677.         0000000000000000  XOR
  678.         ----------------
  679.         1011101010011100
  680.  
  681. * Performing an XOR with a mask of all "0"s will leave all the bits of the
  682.   source value unchanged.
  683.  
  684.  
  685. ---------------------------
  686. Signed and unsigned numbers
  687. ---------------------------
  688.  
  689. The Amiga, like other computers, stores numbers in a binary format, and
  690. as far as the computer is concerned numbers are collections of binary bits.
  691.  
  692. However, there are two distinctions which the computer makes in order to
  693. interpret the binary number data in memory.
  694.  
  695. 1. The size of the number storage space in terms of byte length.
  696.  
  697.    The Amiga CPU recognises all numeric data as being one of 3 sizes:
  698.  
  699.    Byte  8  bits wide
  700.    Word  16 bits wide
  701.    Long  32 bits wide
  702.  
  703. 2. Whether the number is signed or unsigned
  704.  
  705.    The Amiga CPU can carry out arithmetic in two ways:
  706.  
  707.    a. Using twos-complement signed number arithmetic (signed)
  708.    b. Assuming numbers are simple positive integers (unsigned)
  709.  
  710. It is important to be aware whether you are dealing with signed or unsigned
  711. numbers, but remember that this distinction applies only to the operations
  712. which you carry out on the numbers and the way you interpret them.  As far
  713. as the computer is concerned all numbers are stored in an identical way as
  714. collections of binary bits.
  715.  
  716. It is important also to be aware of the limits of numeric values within the
  717. "Byte", "Word", and "Long" number storage sizes.
  718.  
  719. Numeric values lie in the following ranges:
  720.  
  721. Unsigned number ranges:
  722.  
  723.    Byte = 8  bits = 0 to 255           in decimal range
  724.    Word = 16 bits = 0 to 65,535        in decimal range
  725.    Long = 32 bits = 0 to 4,294,967,295 in decimal range
  726.  
  727. Signed number ranges:
  728.  
  729.    Byte = 8  bits = -128         to  127         in decimal range
  730.    Word = 16 bits = -32,768      to  32,767      in decimal range
  731.    Long = 32 bits = -2147483648  to  2147483647  in decimal range
  732.  
  733.  
  734. In HeliOS the standard number size (or "width") for most stack operations
  735. is 16-bit or "Word" length.
  736.  
  737. Any 16-bit numeric value stored on the stack can be interpreted as either
  738. signed or unsigned: the stored binary representation is identical in each
  739. case.
  740.  
  741. Assuming that all the binary bits in the 16-bit binary representation are
  742. set (e.g. 1111111111111111), the number will have the maximum possible value
  743. of decimal 65535 if it is interpreted as an unsigned number.
  744.  
  745. On the other hand, if it is interpreted as a signed number, this maximum
  746. 16-bit binary representation will have the signed value of -1.
  747.  
  748. In case you are not yet fully familiar with the way computer CPUs handle
  749. signed numbers, here is a quick overview.
  750.  
  751. Let us consider 16-bit numbers.
  752.  
  753. So that we can denote a positive or negative number within our number
  754. storage space we designate one bit (the top, or highest bit) as a sign
  755. indicator.  This means that we have to sacrifice one bit from our actual
  756. numeric representation and this reduces the maximum stored numeric value
  757. to half of its unsigned value, as represented by the remaining 15 bits.
  758.  
  759. Since we use the highest bit
  760.  
  761. 1111111111111111
  762. ^
  763. sign bit
  764.  
  765. as a sign indicator, only the remaining bits
  766.  
  767. 1111111111111111
  768.  ^^^^^^^^^^^^^^^
  769.  15 value bits
  770.  
  771. are available to specify the actual number.
  772.  
  773. In 15 bits we can only express a number up to decimal 32767.
  774.  
  775. When the sign bit is set, indicating a negative number, we can have an
  776. equally large number as a negative value.
  777.  
  778. This means that with a signed 16-bit number we can represent a number from
  779. -32768 decimal to +32767 decimal.
  780.  
  781. This matter of using a sign bit is slightly more sophisticated than might
  782. at first appear, and it is not simply a case of setting the sign bit to
  783. indicate the sign of a number.
  784.  
  785. This leads us on to the topic of twos-complement arithmetic.
  786.  
  787. As an illustration, first imagine that you have before you a simple counter
  788. such as you might see on a tape recorder.
  789.  
  790. Let us imagine that the counter has three digits.
  791.  
  792. As the tape winds forward the counter wheels turn and the number increases
  793. up to a maximum value and then starts again from 0.
  794.  
  795. If the counter set to 0 and then the tape is wound backwards, the first
  796. number to appear on the counter will be 999, followed by 998, 997 etc.
  797.  
  798. Since the first number to appear as we started backwrds was 999, and since
  799. our counter does not actually display "-" numbers, we could say that 999 is
  800. equivalent, for all intents and purposes, to -1.  In a similar way, we can
  801. say that 998 is equivalent to -2, and so on.
  802.  
  803. The representation of signed numbers in a computer is very similar.
  804.  
  805. Let us start with the number
  806.  
  807. 0000000000000000  = 0
  808.  
  809. Going forward by one from "0" we would get
  810.  
  811. 0000000000000001  = 1
  812.  
  813. Going backward by one from "0" we would get
  814.  
  815. 1111111111111111  = -1 (signed) = 65535 (unsiged)
  816.  
  817. Going backward by one more we would get
  818.  
  819. 1111111111111110  = -2 (signed) = 65534 (unsigned)
  820.  
  821. This may be comprehensible, but it may also seem a little arbitrary, so
  822. let us see how the scheme can be useful when doing signed arithmetic.
  823.  
  824. Here we have a simple arithmetic operation:
  825.  
  826. 2 - 1 = 2 + (-1) = ?
  827.  
  828. or
  829.  
  830.      2
  831.     -1 +
  832.    ___
  833.  
  834.      ?
  835.  
  836. (Subtracting one from two is the same as adding two plus negative one.)
  837.  
  838. In binary notation the two looks like this:
  839.  
  840. 0000000000000010
  841.  
  842. In binary notation, as we have seen the -1 looks like this:
  843.  
  844. 1111111111111111
  845.  
  846. The computer can simply add these numbers in the normal way, so that when
  847. the total of any column exceeds one, it carries a one into the next column,
  848. and so on.
  849.  
  850. Here is the result of the above example operation:
  851.  
  852.    0000000000000010
  853.  + 1111111111111111
  854.    ----------------
  855.   10000000000000001
  856.   ^
  857.   Final carry out, which is dropped, or "lost"
  858.  
  859. As you can see the computer has carried a 1 into every column, all the way
  860. across and ended up with a one in the seventeenth place.
  861.  
  862. Since the stack is only 16 bits wide the result is truncated:
  863.  
  864. 0000000000000001 = 1
  865.  
  866. This is the right answer, and you can see that this simple convention for
  867. representing negative numbers allows the computer to carry out arithmetic
  868. operations very easily.
  869.  
  870. If you want to learn more about twos-complement arithmetic there are many
  871. texts on computing topics which discuss the matter in more depth.
  872.  
  873. For now, you might be interested to see the relationship between positive
  874. and negative signed numbers:
  875.  
  876. 0000000000000001 = 1
  877. 1111111111111111 = -1
  878.  
  879. 0000000000000010 = 2
  880. 1111111111111110 = -2
  881.  
  882. 0000000000000011 = 3
  883. 1111111111111101 = -3
  884.  
  885. Can you see the relationship between a positive number and its negative
  886. counterpart in twos-complement arithmetic?
  887.  
  888. Here is the simple rule:
  889.  
  890. To change a positive number into its twos-complement negative counterpart
  891. you must:
  892.  
  893. 1. Flip all the bits so that all "1"s become "0"s and all "0"s become "1"s.
  894.  
  895. 2. Add 1
  896.  
  897. 3. Ignore any high-order carry
  898.  
  899. Try it!
  900.  
  901.  
  902. --------------------------------------
  903. Displaying signed and unsigned numbers
  904. --------------------------------------
  905.  
  906. HeliOS has a range of functions for printing numbers to the screen, and
  907. these include both signed and unsigned variants.
  908.  
  909. In general, unsigned number printing functions contain a "U" to indicate
  910. that they are unsigned operators.
  911.  
  912. Here are a few examples:
  913.  
  914. .    Print a signed 16-bit number
  915.  
  916. D.   Print a signed 32-bit number
  917.  
  918. U.   Print an unsigned 16-bit number
  919.  
  920. UD.  Print an unsigned 32-bit number
  921.  
  922. There are more number output functions, and you can find descriptions of
  923. them in the "Dictionary.doc" file.
  924.  
  925.  
  926. -------------------------------------
  927. Comparing signed and unsigned numbers
  928. -------------------------------------
  929.  
  930. It is well worth while looking in the "Dictionary.doc" section devoted to
  931. "Comparisons and Tests" in order to familiarise yourself with the various
  932. number comparison operators provided by HeliOS.
  933.  
  934. You must always ensure that you are testing numbers of the same kind, and
  935. you should also always make sure that signed numbers lie within the legal
  936. range for the size of number you are using (Byte, Word, or Long).
  937.  
  938. Failure to distinguish signed and unsigned numbers is a frequent cause of
  939. problems for beginners when they are testing or comparing numbers which are
  940. potentially negative.
  941.  
  942. Always remember that, for example, if you are using a 16-bit number storage
  943. space you must be careful once you start using numbers which can possibly
  944. exceed 32,767.  This is because any number larger than this falls within
  945. the range of "potentially" negative numbers, and you may run into trouble
  946. if you have been a little careless over number test operators.
  947.  
  948. For example, your program may include a numeric size test like this:
  949.  
  950. APPLES @ ORANGES @ > IF...............
  951.  
  952. Look at how this works as you increase the values of the variables:
  953.  
  954. 200   APPLES ! 100   ORANGES !        APPLES @ ORANGES @ >     Result = 1
  955. 2000  APPLES ! 1000  ORANGES !        APPLES @ ORANGES @ >     Result = 1
  956. 20000 APPLES ! 10000 ORANGES !        APPLES @ ORANGES @ >     Result = 1
  957. 30000 APPLES ! 10000 ORANGES !        APPLES @ ORANGES @ >     Result = 1
  958. 40000 APPLES ! 10000 ORANGES !        APPLES @ ORANGES @ >     Result = 0
  959.  
  960. Can you see why this goes wrong?
  961.  
  962. Yes, 40000, as a "signed" value, is actually -25536, and we were using a
  963. signed comparison operator!
  964.  
  965. Of course, if you use an unsigned comparison, everything is OK:
  966.  
  967. 40000 APPLES ! 10000 ORANGES !        APPLES @ ORANGES @ U>    Result = 1
  968.  
  969. This is a very important point, and if you cannot quite see why there is a
  970. potential problem here we recommend that you read again the documentation
  971. on signed and unsigned numbers, with particular attention the the ranges of
  972. values available for each size of number (Byte, Word, or Long).
  973.  
  974. Here again is the important information concerning size ranges:
  975.  
  976. Unsigned number ranges:
  977.  
  978.    Byte = 8  bits = 0 to 255           in decimal range
  979.    Word = 16 bits = 0 to 65,535        in decimal range
  980.    Long = 32 bits = 0 to 4,294,967,295 in decimal range
  981.  
  982. Signed number ranges:
  983.  
  984.    Byte = 8  bits = -128         to  127         in decimal range
  985.    Word = 16 bits = -32,768      to  32,767      in decimal range
  986.    Long = 32 bits = -2147483648  to  2147483647  in decimal range
  987.  
  988.  
  989. --------------------------------------
  990. Logical and Arithmetic number shifting
  991. --------------------------------------
  992.  
  993. Logical shifting of numbers means that we perform a bitwise shift of
  994. the number like this:
  995.  
  996. 1111111100000000
  997.  
  998. Logically shifed right by 1 position, becomes
  999.  
  1000. 0111111110000000
  1001.  
  1002. Logically shifed right by 1 more position, becomes
  1003.  
  1004. 0011111111000000
  1005.  
  1006. etc.
  1007.  
  1008. In other words, the bits are shifted along to the right and the empty bit
  1009. position on the far left is filled by a 0.
  1010.  
  1011. Logical shifting to the left is similar:
  1012.  
  1013. 1111111100000000
  1014.  
  1015. Logically shifed left by 1 position, becomes
  1016.  
  1017. 1111111000000000
  1018.  
  1019. and again....
  1020.  
  1021. 1111110000000000
  1022.  
  1023. This is very straightforward.
  1024.  
  1025. Interestingly, and most usefully, the fact is that this logical shift
  1026. operation actually performs division and multiplication by powers of 2.
  1027.  
  1028. Because a logical shift operation is very fast, and because multiplication
  1029. and division are very slow, you can appreciate that whenever you want to do
  1030. any multiplication or division by powers of 2, logical shifting is the best
  1031. way of doing it.
  1032.  
  1033. Look at this:
  1034.  
  1035. 0000000000000001 = 1
  1036.  
  1037. 1<-LSL
  1038.  
  1039. 0000000000000010 = 2
  1040.  
  1041. 1<-LSL
  1042.  
  1043. 0000000000000100 = 4
  1044.  
  1045. 1->LSR
  1046.  
  1047. 0000000000000010 = 2
  1048.  
  1049. 1->LSR
  1050.  
  1051. 0000000000000001 = 1
  1052.  
  1053. It really works!
  1054.  
  1055. Now look at this
  1056.  
  1057. 0000000000000001 = 1
  1058.  
  1059. 1<-LSL = 0000000000000010 = 2  = 1 * (2)
  1060. 2<-LSL = 0000000000000100 = 4  = 1 * (2*2)
  1061. 3<-LSL = 0000000000001000 = 8  = 1 * (2*2*2)
  1062. 4<-LSL = 0000000000010000 = 16 = 1 * (2*2*2*2)
  1063.  
  1064. It works in a similar way for division and LSR.
  1065.  
  1066. Try it.....
  1067.  
  1068. This is a marvellous way of quickly effecting division and multiplication
  1069. by factors of 2, and you should use it wherever possible.
  1070.  
  1071. The above was really great for unsigned numbers, but look what happens if
  1072. we try this on signed numbers:
  1073.  
  1074. 1111111111111101 = -3
  1075.  
  1076. 1->LSR
  1077.  
  1078. 0111111111111110 = 32766
  1079.  
  1080. Alas, we have shifted across the sign bit and in so doing we have destroyed
  1081. the essential sign information.
  1082.  
  1083. Obviously this is no use, but fortunately we have another shift operation
  1084. which actually is clever enough to preserve the sign bit.
  1085.  
  1086. This is the "arithmetic" shift operation.
  1087.  
  1088. In the "arithmetic shift right" operation the sign bit is preserved as well
  1089. as being shifted across into the next bit position to the right.
  1090.  
  1091. In the "arithmetic shift left" operation the sign bit is not preserved, but
  1092. a special flag is set in the MC680xx CPU to indicate the status of the sign
  1093. bit before and after the operation.
  1094.  
  1095. Look first at how the arithmetic right shift works:
  1096.  
  1097. 1111111111111100 = -4
  1098.  
  1099. 1->ASR     (= -4 divided by 2)
  1100.  
  1101. 1111111111111110 = -2
  1102.  
  1103. which is correct.
  1104.  
  1105. Arithmetic shifting right on the MC680xx works correctly except for a
  1106. peculiar rounding effect:
  1107.  
  1108. 1111111111111101 = -3
  1109.  
  1110. 1->ASR     (= -3 divided by 2)
  1111.  
  1112. 1111111111111110 = -2
  1113.  
  1114. You can see that, effectively, numbers not divisible by two are divided as
  1115. if pre-decremented to the next negative multiple of two.
  1116.  
  1117. This is a quirk which you need to take account of when using ASR for quick
  1118. division.
  1119.  
  1120. Look also at what happens with -1:
  1121.  
  1122. 1111111111111111 = -1
  1123.  
  1124. 1->ASR     (= -1 divided by 2)
  1125.  
  1126. 1111111111111111 = -1
  1127.  
  1128. As you can see, this is another peculiar result of the above mentioned
  1129. "quirk", and requires care in use.
  1130.  
  1131. In the arithmetic shift left operation on the MC680xx, as we said above,
  1132. the sign bit is not preserved, but a special status flag is set.
  1133.  
  1134. In order to represent this situation in HeliOS, the ASL function returns a
  1135. flag on the stack indicating whether the sign bit changed as a result of the
  1136. ASL operation.  A flag value of "1" indicates that the sign bit changed, a
  1137. flag value of "0" indicates that the sign bit did not change.
  1138.  
  1139. There is one last important thing to note about these shift operations.
  1140.  
  1141. The HeliOS "fast multiply and divide by factor of two" operators, namely
  1142. 2*, D2*, 2/, D2/, 4*, D4*, 4/, D4/ all use simple ASR and ASL operations.
  1143.  
  1144. This is fine in most cases, but you must be aware that the quirks mentioned
  1145. above all apply to these HeliOS fast operators, and be aware also that the
  1146. fast "ASL-based" multipliers do not provide a flag for sign change.
  1147.  
  1148.  
  1149. -------------
  1150. Numeric bases
  1151. -------------
  1152.  
  1153. As far as the computer is concerned numbers are stored and manipulated as
  1154. simple binary values, and only when they are represented to the "outside
  1155. world" do they get translated into nice humanly readable decimal numbers.
  1156.  
  1157. For the purposes of easy manipulation of particular numbers by programmers
  1158. or program users, some circumstances require that numbers be entered into
  1159. the computer, or displayed, in number bases other than decimal.
  1160.  
  1161. For example, it is easier to work with hexadecimal numbers in many cases,
  1162. because this form of number still retains a visual relationship to the
  1163. natural binary used by the computer, but is much more easiliy readable
  1164. than a simple string of "0"s and "1"s.
  1165.  
  1166. It is much easier to convert binary to hexadecimal than binary to decimal
  1167. because sixteen is an even power of two while ten is not.  The same is true
  1168. with octal, so programmers usually use hex or octal to express the binary
  1169. numbers that the computer uses for things like addresses and machine codes.
  1170.  
  1171. Here is a simple comparison table:
  1172.  
  1173. DECIMAL         BINARY          HEXADECIMAL
  1174.  
  1175. 0               0000            0
  1176. 1               0001            1
  1177. 2               0010            2
  1178. 3               0011            3
  1179. 4               0100            4
  1180. 5               0101            5
  1181. 6               0110            6
  1182. 7               0111            7
  1183. 8               1000            8
  1184. 9               1001            9
  1185. 10              1010            A
  1186. 11              1011            B
  1187. 12              1100            C
  1188. 13              1101            D
  1189. 14              1110            E
  1190. 15              1111            F
  1191.  
  1192. Note:
  1193.  
  1194. There is a convention that a "$" symbolic prefix is used to indicate that 
  1195. a number is in hexadecimal format, and a "%" symbolic prefix is used to 
  1196. indicate that a number is in binary format.
  1197.  
  1198. e.g.
  1199.  
  1200. $FFFE          -> Hexadecimal
  1201.  
  1202. %101001010001  -> Binary
  1203.  
  1204.  
  1205. Let us now take a single-length (16-bit) binary number:
  1206.  
  1207. 0111101110100001
  1208.  
  1209. To convert this number to hexadecimal we first subdivide it into four
  1210. units of four bits each:
  1211.  
  1212. 0111 1011 1010 0001
  1213.  
  1214. then convert each 4-bit unit to its hex equivalent:
  1215.  
  1216. 7 B A 1
  1217.  
  1218. or simply 7BA1.
  1219.  
  1220. In HeliOS it is easy to change number bases by simply setting a variable
  1221. called BASE to the appropriate value:
  1222.  
  1223. 2  BASE !L  -> Sets numeric base to Binary
  1224. 8  BASE !L  -> Sets numeric base to Octal
  1225. 10 BASE !L  -> Sets numeric base to Decimal
  1226. 16 BASE !L  -> Sets numeric base to Hexadecimal
  1227.  
  1228. Actually, there is an even more convenient set of words which automatically
  1229. set the numeric base to commonly required values:
  1230.  
  1231. BIN         -> Sets numeric base to 2  = Binary
  1232. DECIMAL     -> Sets numeric base to 10 = Decimal
  1233. HEX         -> Sets numeric base to 16 = Hexadecimal
  1234.  
  1235. This is fine, but there is a subtlety involved here when we consider what
  1236. happens to numbers while we are compiling colon definitions.
  1237.  
  1238. Because HeliOS is a compiler AND an interpreter, it is necessary to fully
  1239. understand the distinction between "run-time" and "compile-time" actions of
  1240. certain HeliOS functions.  This applies particularly to the numeric base
  1241. operators, and below we will give some examples which you should study
  1242. very carefully: this is a VERY important topic.
  1243.  
  1244. We will assume, by the way, in all these examples, that the numeric base
  1245. is always set to DECIMAL at the start.
  1246.  
  1247. Look at this simple line of code, which might be entered at the command
  1248. line or in your source code:
  1249.  
  1250. ." Here is the number 3 expressed in HEX: " 3 HEX . DECIMAL WAITSPACE
  1251.  
  1252. Now look at this line:
  1253.  
  1254. ." Here is the number 3 expressed in BINARY: " 3 BIN . DECIMAL WAITSPACE
  1255.  
  1256. No problem at all here.
  1257.  
  1258. Now let us make a colon definition:
  1259.  
  1260.  : SHOW-3-HEX
  1261.  
  1262.  ." Here is the number 3 expressed in HEX: " 3 HEX . DECIMAL WAITSPACE
  1263.  ;
  1264.  
  1265. Ok, there is still no problem.
  1266.  
  1267. Now let us make another colon definition, this time showing a DECIMAL
  1268. equivalent of the HEX number "$FF":
  1269.  
  1270.  : SHOW-$FF-DECIMAL
  1271.  
  1272.  ." Here is the HEX number $FF expressed in DECIMAL: "
  1273.  
  1274.  HEX FF DECIMAL .
  1275.  WAITSPACE
  1276.  ;
  1277.  
  1278. This does not work!
  1279.  
  1280. Can you see why?
  1281.  
  1282. Yes, the problem is that the change to the HEX numeric base is not carried
  1283. out as the compilation of the colon definition occurs (at compile-time),
  1284. but will only take place when the colon definition executes (at run-time).
  1285. However, the number "FF" is INTERPRETED AT COMPILE-TIME, when the numeric
  1286. base is still set to DECIMAL.
  1287.  
  1288. This means that the number "FF", which we have specified in hexadecimal,
  1289. is not recognised because the compiler is trying to interpret it using the
  1290. current DECIMAL number base, and it is not a valid decimal number.
  1291.  
  1292. The distinction between run-time and compile-time is very important and
  1293. needs to be carefully studied.  As we have just seen, it is particularly
  1294. important when you need to supply numbers in alternate number systems
  1295. within a colon definition.
  1296.  
  1297. Without going into detail here (see the dictionary for more information),
  1298. there are several points of interest about run-time and compile-time.
  1299.  
  1300. 1. Most words, when inside a colon definition, will be compiled rather than
  1301.    executed: this is the very essence of compilation, of course.
  1302.  
  1303. 2. There are some words which are IMMEDIATE words, which means that they
  1304.    always execute immediately, even within colon definitions.
  1305.  
  1306. 3. There are some words which are STATE SENSITIVE words, which means that
  1307.    they behave "cleverly", acting differently whether thay are in a colon
  1308.    definition or not.
  1309.  
  1310. 4. There are methods of forcing an IMMEDIATE word to be compiled.
  1311.  
  1312. 5. There are methods of forcing words to be executed rather than compiled
  1313.    when inside colon definitions.
  1314.  
  1315. All this means that you have a comprehensive toolkit within HeliOS to help
  1316. handle any possible run-time/compile-time contingency.
  1317.  
  1318. Let us look again at our "failed" example:
  1319.  
  1320.  : SHOW-FF-DECIMAL
  1321.  
  1322.  ." Here is the HEX number FF expressed in DECIMAL: "
  1323.  
  1324.  HEX FF DECIMAL .
  1325.  WAITSPACE
  1326.  ;
  1327.  
  1328. This would work fine if we could force the HEX and DECIMAL commands to be
  1329. executed at compile time rather than compiled into the colon definition.
  1330.  
  1331. We can do this quite easily using the special HeliOS commands "[" and "]".
  1332.  
  1333. These are defined as follows:
  1334.  
  1335.      [             ( _ _ _ )
  1336.  
  1337.                    Leave compile mode and start interpreting.
  1338.  
  1339.  
  1340.      ]             ( _ _ _ )
  1341.  
  1342.                    Leave interpret mode and start compiling.
  1343.  
  1344. Look at this new definition:
  1345.  
  1346.  : SHOW-FF-DECIMAL
  1347.  
  1348.  ." Here is the HEX number FF expressed in DECIMAL: "
  1349.  
  1350.  [ HEX ] FF [ DECIMAL ] DECIMAL .
  1351.  WAITSPACE
  1352.  ;
  1353.  
  1354. This will now work fine.
  1355.  
  1356. Notice that we included an extra "DECIMAL" word in the "compiled" part of
  1357. the code, and this will ensure that no matter what the current number base
  1358. might be at run-time, the number will always be printed as decimal.
  1359.  
  1360. This could be made even safer:
  1361.  
  1362.  : SHOW-FF-DECIMAL
  1363.  
  1364.  ." Here is the HEX number FF expressed in DECIMAL: "
  1365.  
  1366.  BASE @L
  1367.  
  1368.  [ HEX ] FF [ DECIMAL ] DECIMAL .
  1369.  
  1370.  BASE !L
  1371.  
  1372.  WAITSPACE
  1373.  ;
  1374.  
  1375. Let us look just what is happening here:
  1376.  
  1377. 1. We read the string and compile it, ready to be printed at RUN-TIME
  1378.  
  1379. 2. We compile words to get the current BASE at RUN-TIME onto the stack
  1380.  
  1381. 3. We switch to HEX numeric base at COMPILE-TIME
  1382.  
  1383. 4. We interpret the number at COMPILE-TIME and compile it as a literal
  1384.  
  1385. 5. We switch back to DECIMAL numeric base at COMPILE-TIME
  1386.  
  1387. 6. We compile words to set the current BASE at RUN-TIME to DECIMAL
  1388.  
  1389. 7. We compile words to print the number at RUN-TIME
  1390.  
  1391. 8. We compile words to restore the previous BASE at RUN-TIME from the stack
  1392.  
  1393.  
  1394. This is all very well, and is quite effective as far as it goes.
  1395.  
  1396. In fact, that was as far as traditional FORTH did go!
  1397.  
  1398. However, HeliOS includes some clever "state sensitive" words which help
  1399. make number entry in different numeric bases very much easier.
  1400.  
  1401.  
  1402. ---------------------------------------------------------
  1403. The useful numeric base control functions: #B, #D, and #H
  1404. ---------------------------------------------------------
  1405.  
  1406. Look at these very useful word definitions:
  1407.  
  1408.      #B            ( - - - n1 or d1 )               "hash-b"
  1409.  
  1410.                    Converts the next word in the input text stream to a
  1411.                    single or double number using binary numeric base.
  1412.  
  1413.      #D            ( - - - n1 or d1 )               "hash-d"
  1414.  
  1415.                    Converts the next word in the input text stream to a
  1416.                    single or double number using decimal numeric base.
  1417.  
  1418.      #H            ( - - - n1 or d1 )               "hash-h"
  1419.  
  1420.                    Converts the next word in the input text stream to a
  1421.                    single or double number using hexadecimal numeric base.
  1422.  
  1423. These are all state sensitive words, and perform their function within a
  1424. colon definition by compiling the number as a literal, so that at run time
  1425. n1 or d1 will be pushed onto the stack.
  1426.  
  1427. The size of the value left on the stack at run time or compile time depends
  1428. on the size of the number read from the input stream.
  1429.  
  1430. Using these new commands, we can now make our example even easier:
  1431.  
  1432.  : SHOW-FF-DECIMAL
  1433.  
  1434.  ." Here is the HEX number FF expressed in DECIMAL: "
  1435.  
  1436.  #H FF . WAITSPACE
  1437.  ;
  1438.  
  1439.  
  1440. Here is another example, using the new "controlled" number base words in a
  1441. command line:
  1442.  
  1443. #B 11111111111111111111. D. #H FFFF . #D 12345 .
  1444.  
  1445.  
  1446. Or, try executing this section of code:
  1447.  
  1448. (You can just highlight the code and press Left-Amiga-e if you wish)
  1449.  
  1450. SCRCLR
  1451.  
  1452. DECIMAL
  1453.  
  1454. CR
  1455.  
  1456. ."  Decimal      : "
  1457.  
  1458. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1459.  
  1460. HEX
  1461.  
  1462. ."  Hexadecimal  : "
  1463.  
  1464. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1465.  
  1466. BIN
  1467.  
  1468. ."  Binary       : "
  1469.  
  1470. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1471.  
  1472. DECIMAL
  1473.  
  1474. WAITSPACE
  1475.  
  1476.  
  1477. Do you understand what is happening here?
  1478.  
  1479.  
  1480. Now put the same code into a colon definition and try it:
  1481.  
  1482. : NUMBERS
  1483.  
  1484. SCRCLR
  1485.  
  1486. DECIMAL
  1487.  
  1488. CR
  1489.  
  1490. ."  Decimal      : "
  1491.  
  1492. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1493.  
  1494. HEX
  1495.  
  1496. ."  Hexadecimal  : "
  1497.  
  1498. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1499.  
  1500. BIN
  1501.  
  1502. ."  Binary       : "
  1503.  
  1504. #B 11111111111111111111. D. #H FFFF . #D 12345 . CR CR
  1505.  
  1506. DECIMAL
  1507.  
  1508. WAITSPACE
  1509. ;
  1510.  
  1511. If you compile and then execute this definition you will see that it still
  1512. works fine, proving that the words #B, #D, and #H are state sensitive.
  1513.  
  1514.  
  1515. ----------------
  1516. ASCII characters
  1517. ----------------
  1518.  
  1519. If the computer uses binary notation to store numbers, how does it store
  1520. alphabetic characters and other symbols?
  1521.  
  1522. Of course the computer still uses binary storage, but in this special case
  1523. the binary values are interpreted according to a special code, called ASCII,
  1524. which was adopted as an industry standard many years ago.
  1525.  
  1526. ASCII = The American Standard Code for Information Interchange
  1527.             ^        ^        ^        ^           ^
  1528.             A        S        C        I           I   = ASCII
  1529.  
  1530.  
  1531. There are a few interesting things to know about ASCII codes:
  1532.  
  1533. 1. ASCII codes are byte sized.
  1534.  
  1535. 2. The codes below $1F are non-printing special "control" codes.
  1536.  
  1537. 3. The Amiga convention uses a simple $A (decimal 10) as a text "carriage
  1538.    return" control character.
  1539.  
  1540. 4. Upper and lower case alphabetic characters have a single bit difference
  1541.    in their ASCII codes:
  1542.  
  1543.    ASCII "M" = 1001101
  1544.    ASCII "m" = 1101101
  1545.                 ^
  1546.                 Lower case has an extra bit set, but is otherwise identical.
  1547.  
  1548. HeliOS has words which interpret and allow you to display ASCII codes:
  1549.  
  1550. EMIT  Takes an ASCII code on the stack and prints the alphabetic character.
  1551.  
  1552.       e.g.
  1553.  
  1554.       75 EMIT would print out "K"
  1555.  
  1556. ASCII Used in the form:
  1557.  
  1558.       ASCII K
  1559.  
  1560.       to read the following character in the input stream and return the
  1561.       associated ASCII code.
  1562.  
  1563.       e.g.
  1564.  
  1565.       ASCII K . would print "75"
  1566.  
  1567.       ASCII K EMIT would print out "K"
  1568.  
  1569. The "ASCII" command word is state sensitive and works equally in compile or
  1570. direct command mode.
  1571.  
  1572. Of course, all computer strings consist of ASCII codes, and it is useful
  1573. to remember the simple rules mentioned above when manipulating strings,
  1574. reading text files, switching the case of strings etc.
  1575.  
  1576. Let us look at the problem of switching the case of strings.
  1577.  
  1578. We can use the logical AND and OR operators to do this, because in the
  1579. ASCII code system one particular bit (bit 5) is always used to specify
  1580. the different upper and lower case variants of any character.
  1581.  
  1582. As we showed above:
  1583.  
  1584.    ASCII "M" = 01001101
  1585.    ASCII "m" = 01101101
  1586.                  ^
  1587.                  Bit 5 is always set for lower case
  1588.  
  1589. So, look what we can do with the AND operation:
  1590.  
  1591.    ASCII "m" = 01101101
  1592.                11011111  AND
  1593.                --------
  1594.    ASCII "M" = 01001101
  1595.                  ^
  1596.                  We have cleared bit 5
  1597.  
  1598. We used 11011111 as a mask to clear bit 5 of the lower case "m", and this
  1599. neatly converted the character to an upper case "M".
  1600.  
  1601. So, we can use AND to convert from lower to upper case.
  1602.  
  1603. Now let us try using OR to convert from upper to lower case.
  1604.  
  1605. This time we will use, 00100000, the inverse of the previous mask.
  1606.  
  1607.    ASCII "M" = 01001101
  1608.                00100000  OR
  1609.                --------
  1610.    ASCII "m" = 01101101
  1611.                  ^
  1612.                  We have set bit 5
  1613.  
  1614. So, as you can see, we can use OR to convert from upper to lower case.
  1615.  
  1616. One more important point to remember here is that you should always take
  1617. care before performing these case conversion operations that the character
  1618. ASCII code which you are converting lies between the values 65 (="A") and
  1619. 122 (="z").  Remember that values outside this range are not convertible
  1620. between upper and lower case, because the upper and lower case convention
  1621. only applies to the letters of the alphabet.
  1622.  
  1623.  
  1624. -----------------
  1625. Number formatting
  1626. -----------------
  1627.  
  1628. In HeliOS, as in FORTH, you can easily design custom number output formats.
  1629.  
  1630. Look at these ways of outputting numbers, for example:
  1631.  
  1632. $200.00        12/31/86       372-8493        6:32:59      98.6
  1633.  
  1634. These all represent the kinds of output you can create by defining your own
  1635. number-formatting words.
  1636.  
  1637. We will now explain how to do this, but before you read the following please
  1638. look in the "Dictionary.doc" and familiarise yourself with the set of words
  1639. described in the section "NUMERIC OUTPUT".
  1640.  
  1641. In particular, look at the definitions of:
  1642.  
  1643. #
  1644. #S
  1645. <#
  1646. #>
  1647. HOLD
  1648. SIGN
  1649.  
  1650. The simplest number-formatting definition we might write would be:
  1651.  
  1652. : UD.  ( ud - - - )  <#  #S  #>  TYPE ;
  1653.  
  1654. This will print an unsigned double-length number.
  1655.  
  1656. Looking within our word definition, the words "<#" and "#>" signify the
  1657. beginning and the end of the number conversion process, and the entire
  1658. number conversion is being performed by the single word #S.
  1659.  
  1660. The word "#S" converts the value on the stack into ASCII characters, and
  1661. has the following properties:
  1662.  
  1663. 1. It will only create as many digits as are necessary to represent the
  1664.    number.
  1665.  
  1666. 2. It will not produce leading zeroes.
  1667.  
  1668. 3. It always produces at least one digit, which will be zero if the value
  1669.    was zero.
  1670.  
  1671. Let us use our new word:
  1672.  
  1673. 12,345  UD.  -> 12345
  1674. 1-2     UD.  -> 12
  1675. 0.      UD.  -> 0
  1676.  
  1677. Going back to our definition, the word TYPE displays the characters that
  1678. represent the number.
  1679.  
  1680. You can see, if you use the function on two numbers in succession, that
  1681. there is no space generated before or after the numbers:
  1682.  
  1683. 12,345 12,345  UD. UD. -> 1234512345
  1684.                               ^^
  1685.                            No space
  1686.  
  1687. If you wanted to include a space, or even a carriage return, after the
  1688. number display, you could easily do so:
  1689.  
  1690. :  UD.  ( ud - - - )  <#  #S  #>  TYPE SPACE CR ;
  1691.  
  1692. Now, let us say that you have a telephone number on the stack, expressed as
  1693. a 32-bit unsigned integer, and typed in as:
  1694.  
  1695. 01623-554828
  1696.  
  1697. Remember that the hyphen will tell the number input routine that this is
  1698. a 32-bit "double" number.
  1699.  
  1700. Suppose that we now want to output this number in the same format that it
  1701. was input.
  1702.  
  1703. We will define a new function to do this, called .PHONE#:
  1704.  
  1705. : .PHONE# ( ud - - - ) <# # # # # # # 45 HOLD #S #> TYPE SPACE ;
  1706.  
  1707. In the above definition, each use of the word "#" produces one digit, but
  1708. note that the number formatting process works from the right of the final
  1709. number.  This means that the six initial "#"s will be responsible for the
  1710. last six (rightmost) digits of the final output.
  1711.  
  1712. Now look at the number 45 in the definition: this is the ASCII code for
  1713. the hyphen "-", as you can see if you go into the interpreter and type
  1714. "45 EMIT".
  1715.  
  1716. The word HOLD takes this ASCII code and inserts it into the formatted number
  1717. character string at the current position (i.e. 6 digits from the right).
  1718.  
  1719. Of course, if you wish, you can replace the number 45 in our definition
  1720. with the more readable expression "ASCII -", like this:
  1721.  
  1722. : .PHONE# ( ud - - - ) <# # # # # # # ASCII - HOLD #S #> TYPE SPACE ;
  1723.  
  1724. After handling the hyphen we still have 5 more digits to deal with, and
  1725. these are simply handled by using "#S", which will automatically convert
  1726. the rest of the number for us.
  1727.  
  1728. Perhaps you would like to try out our new definition ".PHONE#" now.
  1729.  
  1730. The <# ... #> sequence is called a pictured numeric output phrase, because
  1731. it forms a picture (from right to left) of how you want the number to be
  1732. formatted.
  1733.  
  1734. Another use for this type of pictured number output is when we wish to
  1735. display dates and times, so we can now try formatting an unsigned double
  1736. length number as a date in the following form:
  1737.  
  1738. 07/15/86
  1739.  
  1740. Here is the definition
  1741.  
  1742.     :  .DATE  ( ud - - - )
  1743.  
  1744.     <#
  1745.     # #  ASCII  /  HOLD
  1746.     # #  ASCII  /  HOLD
  1747.     # #
  1748.     #>
  1749.     TYPE SPACE
  1750.     ;
  1751.  
  1752. Let us follow this definition, remembering that it is written in reverse
  1753. order from the output.
  1754.  
  1755. The first phrase
  1756.  
  1757. #  #  ASCII  /  HOLD
  1758.  
  1759. produces the rightmost two digits (representing the year) and the rightmost
  1760. "/" character.  The next occurrence of the same phrase produces the middle
  1761. two digits (representing the day) and the leftmost "/".  Finally  "# #"
  1762. produces the leftmost two digits (representing the month).
  1763.  
  1764. We could just as easily have defined it like this:
  1765.  
  1766. :  /nn  ( ud - - - ud )  # # ASCII  /  HOLD  ;
  1767.  
  1768. :  .DATE  ( ud - - - ) <#  /nn  /nn  #  #  #>  TYPE SPACE  ;
  1769.  
  1770. Since you have control over the conversion process, you can actually convert
  1771. different digits in different number bases, a feature that is very useful
  1772. in formatting such numbers as hours and minutes.
  1773.  
  1774. For example, let us say that you have the time in seconds on the stack and
  1775. you want a word that will print "hh:mm:ss".
  1776.  
  1777. You might define it this way:
  1778.  
  1779. :  SEXTAL  6 BASE !L  ;
  1780. :  :00    ( ud - - - ud )  #  SEXTAL  #  DECIMAL  ASCII  :  HOLD  ;
  1781. :  .TIME  ( ud - - - )    <#  :00 :00 #S #>  TYPE SPACE  ;
  1782.  
  1783. We use the word ":00" to format the seconds and the minutes.
  1784.  
  1785. Both seconds and minutes are modulo-60 so the right digit can go as high
  1786. as nine, but the left digit can only go up to five.
  1787.  
  1788. Thus, in the definition of ":00", we convert the first digit (the one on
  1789. the right) as a decimal number, then we go into "sextal" (base 6) to convert
  1790. the left digit.
  1791.  
  1792. Finally we return to decimal and insert the colon character.
  1793.  
  1794. After ":00" converts the second and the minutes, "#S" converts the remaining
  1795. hours.
  1796.  
  1797. Now, if we have 4,500 seconds stored on the stack, we would get
  1798.  
  1799. 4,500  .TIME   ->   1:15:00
  1800.  
  1801. Note that there are 86,400 seconds in a day: too many for a 16-bit number.
  1802.  
  1803. So far we have formatted only unsigned double-length numbers.
  1804.  
  1805. The <# ... #> form expects only unsigned double-length numbers, but we can
  1806. use if for other types of numbers by making arrangements on the stack.
  1807.  
  1808. For instance let's look at a simplified version of the system definition
  1809. of D. (which prints a signed double-length number):
  1810.  
  1811.  
  1812. :  D.  ( d - - - )
  1813.  
  1814.    DUP>R  DABS  <#  #S  R>  SIGN  #>  TYPE SPACE  ;
  1815.  
  1816. The word SIGN, which must be situated within the pictured numeric output
  1817. phrase, inserts a minus sign in the character string only if the top number
  1818. on the stack is negative.  So, we save a copy of the high-order cell (the
  1819. one with the sign bit) on the return stack for later use, using "DUP>R".
  1820.  
  1821. The word "<#" expects only unsigned double-length numbers, therefore we must
  1822. take the absolute value of our double-length signed number using "DABS".
  1823.  
  1824. We now have the proper arguments for the pictured numeric output phrase.
  1825.  
  1826. Next, the word "#S" converts the digits, right to left.
  1827.  
  1828. Finally, we return the sign-indicator to the data stack using "R>".
  1829.  
  1830. If it is negative, SIGN will add a minus sign to the formatted string.
  1831.  
  1832. Since we want our minus sign to appear at the left, we include SIGN at the
  1833. right of our  <# ... #> phrase, but in some cases, such as accounting, we
  1834. may want a negative number to be written like this:
  1835.  
  1836. 12345-
  1837.  
  1838. in which case we would place the word SIGN at the left side of our main
  1839. <# ... #> phrase like this:
  1840.  
  1841. <#  SIGN  #S  #>
  1842.  
  1843. Let us now define a word that will print a signed double-length number with
  1844. a decimal point and two decimal places to the right of the decimal: this
  1845. could be used for outputting cash in "£"s and pence.
  1846.  
  1847.     : £.  ( d - - - )
  1848.  
  1849.     DUP>R DABS
  1850.     <#
  1851.     # #
  1852.     ASCII . HOLD
  1853.     #S
  1854.     R>  SIGN
  1855.     ASCII £ HOLD
  1856.     #>
  1857.     TYPE SPACE
  1858.     ;
  1859.  
  1860. Let's try it
  1861.  
  1862. 2000.00 £.     -> £2000.00
  1863.  
  1864. or even
  1865.  
  1866. 2,000.00  £.   -> £2000.00
  1867.  
  1868.  
  1869. You can also write special formatting words for single-length numbers.
  1870.  
  1871. For example, if you want to use an unsigned single-length number, simply put
  1872. a zero on the stack before the word "<#".
  1873.  
  1874. This effectively changes the initial single-length number into an equivalent
  1875. double-length number which has nothing (zero) in the high-order cell.
  1876.  
  1877. To format a signed single-length number, again you must supply a zero as a
  1878. high-order cell.  However, you must also save a copy of the signed number
  1879. for SIGN to use, and you must leave the absolute value of the number in the
  1880. second stack position:
  1881.  
  1882. ( n - - - )  DUP>R  ABS  0  <#  #S  R>  SIGN  #.
  1883.  
  1884.  
  1885. The following set-up phrases are needed to print various kinds of numbers:
  1886.  
  1887.  
  1888. NUMBER TO BE PRINTED            PRECEDE  <#  by
  1889.  
  1890. 32-bit number unsigned          (nothing needed)
  1891.  
  1892. 31-bit number and sign bit      DUP>R DABS
  1893.                                 ^^^^^
  1894.                                 To save the sign on the return stack
  1895.                                 for removal just before SIGN
  1896.  
  1897. 16-bit number unsigned          0
  1898.  
  1899.                                 To give a dummy high-order word, thus
  1900.                                 converting to equivalent double number
  1901.  
  1902. 15-bit number and sign bit      DUP>R  ABS  0
  1903.                                 ^^^^^       ^
  1904.                                 |||||       To give a dummy high-order word,
  1905.                                 |||||       thus converting to equivalent
  1906.                                 |||||       double number
  1907.                                 |||||
  1908.                                 To save the sign on the return stack
  1909.                                 for removal just before SIGN
  1910.  
  1911.  
  1912. ------------------------------------------------------------------------
  1913. Using scaled and fractional integer arithmetic instead of floating point
  1914. ------------------------------------------------------------------------
  1915.  
  1916. One of the many idiosyncracies of FORTH (and HeliOS) which make it so
  1917. very different from most other languages is the use of scaled integer
  1918. arithmetic.  FORTH traditionally did not have dedicated floating point
  1919. math functions, and neither does HeliOS: both languages rely on rather
  1920. unusual scaled integer techniques which are far faster and more efficient
  1921. than using floating point calculation.
  1922.  
  1923. Remember again the philosophy of helping the computer to do its work in
  1924. the most efficient way possible: floating point arithmetic is a "human
  1925. methodology" and the computer has to do extra work to try to simulate
  1926. this kind of calculation.
  1927.  
  1928. HeliOS and FORTH use methods of calculation which tie in very efficiently
  1929. with the way the computer functions internally, thus making arithmetic
  1930. processing many times quicker than more "conventional" techniques.
  1931.  
  1932. First of all we need to look at just what we mean by floating point and
  1933. scaled integer calculation.
  1934.  
  1935. Look at the following calculation which you might perform on a desk top
  1936. calculator:
  1937.  
  1938.  
  1939. YOUR ENTRY                CALCULATOR DISPLAY
  1940.  
  1941.  7.60                           7.60
  1942.  x                              7.60
  1943.  2.00                           2.00
  1944.  =                              15.2
  1945.  
  1946. The decimal point "floats" in the sense that it will end up in the correct
  1947. display position to discriminate between whole and partial elements of the
  1948. number.  This is known as "floating point" arithmetic and we are all very
  1949. familiar with these methods of expression in everyday calculations.
  1950.  
  1951. Floating-point format can also be seen in the way scientific notation keeps
  1952. account of large numbers by remembering a numeric element combined with a
  1953. number of decimal places.
  1954.  
  1955. Thus, in scientific notation, 12 million might be expressed as:
  1956.  
  1957.                     6
  1958. 12 million = 12 x 10   =  12 times 10 to the power 6
  1959.  
  1960. Ten to the sixth power equals one million.
  1961.  
  1962. In floating point computation 12 million could be stored as the two numbers
  1963. 12 and 6, where it is understood that 6 is the power of ten to be multiplied
  1964. by 12.  In a similar way a number like 1.234 could be stored as 1234 and -3.
  1965.  
  1966. The good thing about floating point representation is that the computer can
  1967. represent an enormous range of numbers using two relatively small numbers.
  1968.  
  1969. On the other hand, scaled integer arithmetic is a method of storing numbers
  1970. in memory without storing the positions of each number's decimal point.  In
  1971. this system all numbers are stored in terms of multiples of the smallest
  1972. unit of definition.  For example, in working with measurements of distance
  1973. where the very smallest denomination would be the "centimetre", all values
  1974. would be stored as multiples of centimetres.
  1975.  
  1976. Here is a comparison between scaled integer and floating point methods of
  1977. numeric representations of metric distance values.
  1978.  
  1979. REAL WORLD              SCALED-INTEGER          FLOATING-POINT
  1980. VALUE                   REPRESENTATION          REPRESENTATION
  1981.  
  1982. 1.23   (metres)         123  (centimetres)      123(-2)
  1983. 10.98                   1098                    1098(-2)
  1984. 100.00                  10000                   1(2)
  1985. 58.60                   5860                    586(-1)
  1986.  
  1987. As you can see, with scaled integers all the values must conform to the
  1988. same scale and all numbers are integers.  If you are using scaled integer
  1989. arithmetic in a computer program, the program needs to insert a decimal
  1990. point in the final on-screen display according to the scale required. Of
  1991. course the program "knows" what basic unit all numbers are derived from,
  1992. so this is no problem.
  1993.  
  1994. With floating point representation the decimal point position is actually
  1995. stored with each number, but this is not necessarily an advantage because
  1996. the program itself will still need to take account of the final display
  1997. requirement for decimal point expression just like the integer version.
  1998.  
  1999. Of course, not all numbers are based on decimal systems, and perhaps you
  2000. can already see that scaled integer arithemetic is going to ultimately
  2001. prove rather more flexible than decimal floating point.
  2002.  
  2003. This whole topic is somewhat controversial, because it so happens that the
  2004. commonly accepted way of doing things is not the best!  This means that the
  2005. two camps argue from differing points of view: one prefers to have superior
  2006. real performance, the other prefers easy conventionality of expression.
  2007.  
  2008. Most computer languages and most programmers use floating point arithmetic
  2009. as an automatic standard, preferring to stick with decimal representations
  2010. and allowing the computer to do most of the work in adapting its internal
  2011. calculations to human conventional and perceptual requirements.
  2012.  
  2013. There is undoubtedly some logic in this, especially when the "man in the
  2014. street" first enters the world of computer programming: it just seems so
  2015. obvious to carry on using our normal conventions when we write computer
  2016. programs....or does it?
  2017.  
  2018. Perhaps so, but perhaps not when we consider things more deeply.
  2019.  
  2020. Making the programmer's life easier is one thing, but in most cases the
  2021. programmer is there to write a computer program to do a particular job,
  2022. and a "good" computer programmer presumably wishes to write a computer
  2023. program which is as efficient as possible.  In this case, should the
  2024. programmer not be prepared to adapt his methods to allow the very best
  2025. software control of the computer?
  2026.  
  2027. Many programming applications require real-time calculations in non-decimal
  2028. number systems: computer games are a good example.
  2029.  
  2030. In fact, it would be fair to say that MOST programming applications require
  2031. real-time calculations in non-decimal number systems.
  2032.  
  2033. Many programs on the Amiga, which are often concerned with the generation
  2034. of real time graphics and sound, need to be as fast as possible to get the
  2035. most out of the computer.
  2036.  
  2037. In these cases (in MOST cases, in fact) a "good" programmer is interested
  2038. in maximizing the efficiency of the computer rather than making the task
  2039. of programming (possibly) easier by adhering in the source code to an
  2040. artificial decimal floating point number representation system.
  2041.  
  2042. In many cases, also, memory efficiency is an important criterion, as well as
  2043. speed, and again here scaled integer arithmetic comes out on top.  If an
  2044. application has to repeatedly perform calculations millions of times every
  2045. second in a time critical environment, scaled integer arithmetic will give
  2046. superior performance.  Floating point multiplication or division can take
  2047. many times as long as its equivalent scaled integer counterpart.
  2048.  
  2049. Also, to perform addition or subtraction, the realignment of the values
  2050. prior to a floating point operation is at least as time-consuming as the
  2051. arithmetic operation itself.  The fact is that the Amiga does not think in
  2052. "floating point" terms, and you pay a heavy penalty for trying to make it
  2053. act as though it does.
  2054.  
  2055. Do not think that integer arithmetic is some "poor relation" only suited to
  2056. arcade game programming.  For many years FORTH programmers have been writing
  2057. complex applications with scaled integer arithmetic involving solutions of
  2058. fancy things such as differential equations, Fast Fourier Transforms, non-
  2059. linear least square fitting, linear regression, and so on.
  2060.  
  2061. Of course FORTH systems can support floating point arithmetic, and some
  2062. do indeed have built-in floating point functions.  HeliOS does not have a
  2063. built-in floating point system, but you can easily incorporate one into
  2064. the language for yourself if you really do prefer to work in this way.
  2065.  
  2066. Calculation ranges and types do also affect which kind of number system
  2067. is preferred, and most problems with physical input and outputs require
  2068. a dynamic range of no more than a few thousand to one, and thus fit
  2069. comfortably in a 16-bit integer word.  Some calculations may actually
  2070. require 32-bit intermediate values, but HeliOS accommodates this easily.
  2071.  
  2072. The "trick" to eliminating wasteful floating point operations from your
  2073. code is to ensure that values are always scaled according to the range of
  2074. possible values that you're interested in.
  2075.  
  2076. HeliOS, along with any FORTH system, provides a powerful toolkit of high
  2077. level commands called "scaling operators" to support techniques such as
  2078. rational approximations and fractional arithmetic.  These techniques are
  2079. immensely powerful and provide unique possibilities for performing the
  2080. kinds of "fraction" calculation which are often more appropriate to real
  2081. world applications that decimal floating point: remember, the real world
  2082. is NOT built from floating point decimal numbers!
  2083.  
  2084. Let us look at the possibilty of performing a HeliOS fractional arithmetic
  2085. calculation using the "*/" function.
  2086.  
  2087. Here is the definition of "*/" :
  2088.  
  2089.  */  ( n1 n2 n3 - - - n4 )
  2090.  
  2091.      Multiplies then divides (n1xn2/n3) using a 32-bit intermediate result.
  2092.  
  2093. As its name implies */ performs multiplication, then division.
  2094.  
  2095. For example, let us assume that the stack contains three numbers:
  2096.  
  2097. ( 225 32 100 - - - )
  2098.  
  2099. If we use "*/" it will first multiply 225 by 32, then divide the result by
  2100. 100.
  2101.  
  2102. The "*/" operator is particularly useful as an integer arithmetic solution
  2103. to problems such as percentage calculations.  For example, you could define
  2104. the word % (percent) like this:
  2105.  
  2106.  :  %  ( n1  %  - - - n2 )
  2107.  
  2108.  100 */
  2109.  
  2110.  ;
  2111.  
  2112. Now, by entering the number 225 and then using
  2113.  
  2114. 32  %
  2115.  
  2116. you would end up with 32% of 225 (which is 72) on the stack.
  2117.  
  2118. Note that "*/" is not just a "*" and a "/" operating successively, but a
  2119. much more powerful operation which uses a double-length intermediate result.
  2120. This means that during its internal calculations "*/" can accept overflow
  2121. into 32-bit values without truncation of values.
  2122.  
  2123. Suppose you want to compute 34% of 2000.
  2124.  
  2125. Remember that single length operators * and / only work with arguments and
  2126. results within the range of -32768 to +32767.
  2127.  
  2128. So, if you were to enter the phrase
  2129.  
  2130. 2000  34  *  100 /
  2131.  
  2132. you would get an incorrect result because the "intermediate result" (in
  2133. this case the result of multiplication) exceeds +32767.
  2134.  
  2135. However, "*/" allows a 32-bit intermediate result, so that its range will
  2136. be large enough to hold the result of any two single-length numbers which
  2137. might be multiplied together.
  2138.  
  2139. The phrase
  2140.  
  2141. 2000 34  100  */
  2142.  
  2143. returns the correct answer because the end result falls within the range of
  2144. single length numbers.
  2145.  
  2146. Another interesting question involved here is that of "rounding".
  2147.  
  2148. Let us look at another hypothetical problem involving percentages.
  2149.  
  2150. If 32% of the students eating at a school cafeteria usually buy bananas,
  2151. how many bananas should be on hand to feed 22 students?
  2152.  
  2153. Naturally we are only interested in whole bananas, so we need to round off
  2154. any partial remainder.
  2155.  
  2156. As our definition of "%" now stands, any fractional percentage value is
  2157. simply dropped.  In other words the result is "truncated".
  2158.  
  2159. Look at these figures:
  2160.  
  2161. 32% OF     ACCURATE ANSWER      RESULT OF OUR % FUNCTION
  2162.  
  2163. 225           72.00             72  = exactly correct
  2164. 226           72.32             72  = truncated, but correct as if rounded
  2165. 227           72.64             72  = truncated, but not rounded correctly
  2166.  
  2167. Ideally, with any decimal value of 0.5 or higher, we want to round upwards
  2168. to the next whole banana.
  2169.  
  2170. Let us now define the word R% for "rounded percent", like this:
  2171.  
  2172.  :  R%   ( n1 % - - - n2 )
  2173.  
  2174.  10 */ 5 + 10 /
  2175.  ;
  2176.  
  2177. Now, using this new function, the expression
  2178.  
  2179. 227 32 R%  .
  2180.  
  2181. will give you 73, which is correctly rounded up.
  2182.  
  2183. Look again at the definition of R%  ->     10 */ 5 + 10 /
  2184.  
  2185. Notice that we first divide by 10 rather than 100, and this gives us larger
  2186. (by a factor of 10) number to work with as a result.
  2187.  
  2188. Next we add the median value of 5, ensuring that we are rounding up fully
  2189. to the next value, then we perform the final division by 10.
  2190.  
  2191. Look at this:
  2192.  
  2193. OPERATION               STACK CONTENTS
  2194.  
  2195.                         227  32  10
  2196.  
  2197. */                      726
  2198.  
  2199. 5  +                    731
  2200.  
  2201. 10 /                    73
  2202.  
  2203. The final division by 10 sets the value to the correct decimal position.
  2204.  
  2205. A disadvantage to this method of rounding is that you lose one decimal
  2206. place of range in the final result;  that is, it can only go as high as
  2207. 3276 rather than 32767.  However if that is a problem you can always use
  2208. double-length numbers and still be able to round.
  2209.  
  2210. Look at this, for example:
  2211.  
  2212. Using "M*/" we can redefine our earlier version of % so that it will accept
  2213. a double-length argument:
  2214.  
  2215. :  %  ( d1 n%  - - -  d2 )  100  M*/  ;
  2216.  
  2217. as in
  2218.  
  2219. 20,050  15 % D.    ->  3007
  2220.  
  2221. We can redefine our earlier definition of R% to get a rounded double-length
  2222. result like this:
  2223.  
  2224. :  R%  ( d1 n% - - - d2 )   10 M*/  5 M+  1 10  M*/  ;
  2225.  
  2226. then
  2227.  
  2228. 20,050  15  R%     -> 3008
  2229.  
  2230. Let us look at another example.
  2231.  
  2232. Take the simple problem of computing two-thirds of 171.
  2233.  
  2234. Basically there are two ways to go about it.
  2235.  
  2236. 1.  We could compute the value of the fraction 2/3 by dividing 2 by 3
  2237.     to obtain the repeating decimal 0.6666666666666666 etc.
  2238.  
  2239.     Then we could multiply this value by 171.
  2240.  
  2241.     The result would be 113.999999999999 etc.
  2242.  
  2243.     This is not quite right, but it could be rounded up to 114.
  2244.  
  2245. 2.  We could multiply 171 by 2 to get 342.
  2246.  
  2247.     Then we could divide this by 3 to get 114.
  2248.  
  2249. Notice that the second way is much simpler and more accurate: it is more
  2250. of a "real world" practical solution.
  2251.  
  2252. Most computer languages support the first way: after all, how many times
  2253. have you seen a "C" or BASIC program using a fraction like two-thirds in
  2254. its calculations.  Of course, it is assumes "by default" that you must
  2255. accept the inefficiency of 0.6666666666666666666666666 etc.
  2256.  
  2257. However, HeliOS supports the second way of doing things!
  2258.  
  2259. The "*/" function lets you easily have a fraction like two-thirds, as in
  2260.  
  2261. 171  2   3  */
  2262.  
  2263. Now let us take a slightly more complex example.
  2264.  
  2265. We want to distribute $150 in proportion to two values:
  2266.  
  2267. 7105            ?
  2268. 5145            ?
  2269. ----            ---
  2270. 12250           150
  2271.  
  2272. Again we could solve the problem this way:
  2273.  
  2274. (7105 / 12250)  x  150
  2275.  
  2276. and
  2277.  
  2278. (5145 / 12250) x 150
  2279.  
  2280. but for greater accuracy we should say:
  2281.  
  2282. (7105 x 150) / 12250
  2283.  
  2284. and
  2285.  
  2286. (5145 x 150) / 12250
  2287.  
  2288. In HeliOS we could say:
  2289.  
  2290. 7105 150 12250  */
  2291.  
  2292. which would give us 87.
  2293.  
  2294. Then we could say:
  2295.  
  2296. 5145  150  12250  */
  2297.  
  2298. which would give us 63.
  2299.  
  2300. It can be said here that the values 87 and 63 are "scaled" to 7105 and 5145.
  2301.  
  2302. Calculating percentages as we did earlier is also a form of scaling, and
  2303. for this reason "*/" is known as a "scaling" operator.
  2304.  
  2305. Another, slightly more powerful, scaling operator in HeliOS is "*/MOD":
  2306.  
  2307.  */MOD   ( n1, n2, n3 - - - remainer, quotient )
  2308.  
  2309.          Multiplies then divides (n1 x n2/n3).
  2310.  
  2311.          Returns the remainder and the quotient.
  2312.  
  2313.          Uses a 32-bit intermediate result.
  2314.  
  2315. As you can see, this function returns a remainder, which can be very useful
  2316. in many applications for rounding etc.
  2317.  
  2318. So far we have only used scaling operations to work on rational numbers,
  2319. but they also can be used on rational approximations of irrational constants
  2320. such as "pi" or "the square root of 2".
  2321.  
  2322. For example the so called "real" value of "pi" is
  2323.  
  2324. 3.14159265358 etc.
  2325.  
  2326. To multiply a number by "pi" yet stay within the bounds of single-length
  2327. arithmetic, we could write
  2328.  
  2329. 31416  10000  */
  2330.  
  2331. and get quite a good approximation.
  2332.  
  2333. :  *PI   ( n1  - - - n2 )  31416  10000  */  ;
  2334.  
  2335. Now let us write a definition to compute the area of a circle, given its
  2336. radius, by using the formula:
  2337.  
  2338. pi * r²
  2339.  
  2340. The value of the radius will be on the stack, so we DUP it and multiply it
  2341. by itself then multiply by "pi":
  2342.  
  2343. :  AREA  ( radius - - - area )  DUP  *  *PI ;
  2344.  
  2345. Try it with a circle whose radius is 10 inches:
  2346.  
  2347. 10  AREA
  2348.  
  2349. The result is 314.
  2350.  
  2351. For even more accuracy we might wonder if there is a pair of integers other
  2352. than 31416 and 10000 that is a closer approximation to "pi", and actually
  2353. there is.
  2354.  
  2355. The fraction
  2356.  
  2357. 355  113  */
  2358.  
  2359. is accurate to more than six places beyond the decimal, as opposed to less
  2360. than four places with 31416!
  2361.  
  2362. A new and improved definition can now say:
  2363.  
  2364. :  PI  ( n1 - - - n2 )  355 113 */  ;
  2365.  
  2366. It turns out that you can approximate nearly any constant by many different
  2367. pairs of integers, all numbers less than 32768, with an error of less than
  2368. 10 to the power -8.  This is very interesting: just think about it and you
  2369. will appreciate that there is here a great potential method for very fast 
  2370. and very accurate computation.
  2371.  
  2372. We have just seen how to express fractions as a pair of integers when we
  2373. are scaling, but some applications require non integer values in situations
  2374. other than scaling.
  2375.  
  2376. For instance, how might you add these two fractions
  2377.  
  2378. 7         23
  2379. --   +    --   =
  2380. 34        99
  2381.  
  2382. without using floating point?
  2383.  
  2384. We can do it in HeliOS with a simple technique called fractional arithmetic,
  2385. which is sometimes also called "fixed-point" arithmetic.
  2386.  
  2387. Fixed-point arithmetic involves scaling and an implied decimal point.
  2388.  
  2389. Instead of scaling by multiples of ten (which we humans are used to) we
  2390. scale by multiples of two (which computers are much better at).
  2391.  
  2392. In this technique the implied decimal point might be more aptly called a
  2393. 'binary point'.
  2394.  
  2395. Suppose we define
  2396.  
  2397.  +1 CONSTANT 16384
  2398.  
  2399. where 16384 is a scaled value equivalent to 1.
  2400.  
  2401. In binary 16384 looks like this:
  2402.  
  2403. 0100000000000000
  2404.  
  2405. The 1 is scaled up in binary along with the implied binary point.
  2406.  
  2407. Now let us extend HeliOS to define two new math operators - "fractional
  2408. multiply" and "fractional divide", based on our new scale:
  2409.  
  2410. :  *.  ( n1 n2 - - - n3 )    +1   */ ;
  2411.  
  2412. :  /.  ( n1 n2 - - - n3 )    +1   SWAP  */ ;
  2413.  
  2414. To demonstrate these new functions, we can start simply.
  2415.  
  2416. If we divide 1 by 1 we should get 1:
  2417.  
  2418. 1  1  /.
  2419.  
  2420. which gives a result of 16384, which is our scaled unit value, since 16384
  2421. represents positive 1.
  2422.  
  2423. If we divide 1 by 2. with:
  2424.  
  2425. 1  2  /.
  2426.  
  2427. we get a result of 8192.
  2428.  
  2429. Here 8192 represents the value "one-half", since it is half of 16384.
  2430.  
  2431. Let us go back to the problem:
  2432.  
  2433. 7         23
  2434. --   +    --   =
  2435. 34        99
  2436.  
  2437. We can now solve the problem like this:
  2438.  
  2439. 7  34  /.  23  99  /.  +
  2440.  
  2441. Notice that the final operator is simply "+" to add the two fractions.
  2442.  
  2443. Of course this is not yet the whole solution, since the answer is not yet
  2444. expressed in a useful final form.
  2445.  
  2446. To scale the result back to decimal form we now use
  2447.  
  2448. 10000   *.
  2449.  
  2450. to give the result 4381.
  2451.  
  2452. Our answer, then, is "4381/10000", better known as "0.4381".
  2453.  
  2454. With fractional arithmetic we can add, subtract, multiply and divide using
  2455. fast integer operations, even though we are dealing with non-integer values.
  2456.  
  2457. In number-crunching applications which do a lot of arithmetic computation
  2458. the speed of these internal calculations is often crucial, and the methods
  2459. described here can have a huge impact of software performance.
  2460.  
  2461. By contrast, the problem of converting numbers to human-readable (decimal)
  2462. form is relatively unimportant, because only the final result needs to be
  2463. displayed.  In fact, with applications such as graphics and sound the result
  2464. never needs to be converted to decimal form but, rather, remains in a form
  2465. suitable to the computer.
  2466.  
  2467. Nevertheless, we might still want to input and output these fractions using
  2468. customary decimal notation.
  2469.  
  2470. If you want HeliOS to print the result of the above calculation with the
  2471. decimal in the correct place, all you need is a some nice customised number
  2472. formatting words such as this:
  2473.  
  2474. :  #.####    DUP ABS 0 <#  # # # #  46 HOLD  # ROT SIGN  #>  TYPE SPACE  ;
  2475.  
  2476. :  .F   ( fraction - - - )  10000  *.  #.#### ;
  2477.  
  2478. Now the complete statement of our problem could be:
  2479.  
  2480. 7  34  /.  23  99  /.  + .F
  2481.  
  2482. This would print out
  2483.  
  2484. 0.4381
  2485.  
  2486. This has not used floating point calculation at all, but it has produced
  2487. the same result a very great deal faster.
  2488.  
  2489. What if we want to express input arguments as floating point numbers?
  2490.  
  2491. Let us say we want to accept, for example
  2492.  
  2493. .1250 + .3750 = ?
  2494.  
  2495. To do this in HeliOS we first need a word to convert a number with a decimal
  2496. point into a scaled fraction:
  2497.  
  2498. :  D>F  ( d1 -- fraction )   DROP  10000 /.  ;
  2499.  
  2500. (D>F stands for double-number to fraction.)
  2501.  
  2502. Now we can enter
  2503.  
  2504. .1250  D>F  .3750  D>F  +  .F
  2505.  
  2506. This would give the result
  2507.  
  2508. 0.5000
  2509.  
  2510. Notice that we MUST express the inputs complete to four decimal places.
  2511.  
  2512. We can multiply two fractions with  *. like this:
  2513.  
  2514. .7500  D>F  .5000  D>F   *.  .F
  2515.  
  2516. This would give the result
  2517.  
  2518. .3750
  2519.  
  2520. Interestingly, if we use *. to multiply a fraction by an integer, the result
  2521. is an integer.
  2522.  
  2523. For example
  2524.  
  2525. 28  .5000   D>F   *.
  2526.  
  2527. gives a result of 14.
  2528.  
  2529. With /.  we can divide say  -0.3 by 0.95 like this
  2530.  
  2531. -.3000  D>F   .9500  D>F   /.   .F
  2532.  
  2533. This would give the result
  2534.  
  2535. -0.3160
  2536.  
  2537. We can also get a fraction result by using  /. on two integers.
  2538.  
  2539. Thus
  2540.  
  2541. 22  44  /.   .F   
  2542.  
  2543. would give the result
  2544.  
  2545. .5000
  2546.  
  2547. We also get an integer result if we  /.  an integer by a fraction.
  2548.  
  2549. To summarise, using the letter "f" for fraction and "i" for integer, the
  2550. following input/output combinations of fractional numbers or combinations
  2551. of fractions and integers are possible:
  2552.  
  2553. f f  +   gives  f
  2554. f f  -   gives  f
  2555. f i  *   gives  f
  2556. i f  *   gives  f
  2557. f i  /   gives  f
  2558. f f  *.  gives  f
  2559. f i  *.  gives  i
  2560. i f  *.  gives  i
  2561. f f  /.  gives  f
  2562. i i  /.  gives  f
  2563. i f  /.  gives  i
  2564.  
  2565. Using a binary oriented scalar such as 16384 instead of a decimal number
  2566. such as 10000 allows greater accuracy within 16 bits (by a ratio of 16 to
  2567. 10).
  2568.  
  2569. Also,  *. and  /.  can be coded in assembler extremely efficiently.
  2570.  
  2571. However, the choice of 16384 as the value of "1" is quite arbitrary.
  2572.  
  2573. Had we chosen 256, we would get 8 bits (including sign) to the left, and
  2574. eight bits to the right of the binary point.
  2575.  
  2576. This same technique can be extended of course into 32 bit numbers if that
  2577. kind of precision is really needed.
  2578.  
  2579. A great use for  *.  and /.  is with trig functions, since an angle can
  2580. be represented internally as a fraction of a circle (well-behaved between
  2581. 0 and 1).
  2582.  
  2583. Conversions to and from degrees become easy as well.
  2584.  
  2585. Here we have very briefly introduced scaling operators, rounding, rational
  2586. approximations and fixed-point technique.
  2587.  
  2588. There is nothing to prevent you from adding floating point functions to
  2589. HeliOS if you wish, but it would be best first to at least try the virtues
  2590. of compactness, high performance, simplicity and elegance afforded by the
  2591. existing arithmetic tools.  To do this requires a rigorous and continual
  2592. rejection of anything that is not absolutely necessary, but by the judicious
  2593. use of scaling and double-length integers where required you can indeed
  2594. eliminate the expense of floating point operations from your code.
  2595.  
  2596. You might prefer to add floating point functions if:
  2597.  
  2598. 1.  You want to use your computer like a calculator for 1-shot computations.
  2599.  
  2600. 2.  You value the initial programming time more highly than the execution
  2601.     time spent whenever the calculation is performed.
  2602.  
  2603. 3.  You need a number to be able to describe a very large dynamic range
  2604.     (greater than -2 billion to +2 billion).
  2605.  
  2606. How often do you think these criteria will be important?
  2607.  
  2608. ---------------------
  2609. Examples and problems
  2610. ---------------------
  2611.  
  2612. 1.  Write a word which uses a BEGIN....UNTIL loop to determine the upper
  2613.     (positive) and lower (negative) limit for a signed single-length number.
  2614.  
  2615.     Check the results against the values given above.
  2616.  
  2617. 2.  Define a word which converts a bit number into a mask corresponding to
  2618.     that bit.
  2619.  
  2620.     For example:
  2621.  
  2622.     0 BIT  should give the result 1 (decimal) or 1   (binary)
  2623.     1 BIT  should give the result 2 (decimal) or 10  (binary)
  2624.     2 BIT  should give the result 8 (decimal) or 100 (binary)
  2625.     etc.
  2626.  
  2627. 3.  Define a word which turns on a specified bit in a number on the stack.
  2628.  
  2629.     The stack effect would be ( n1, Bit# - - - n2 ).
  2630.  
  2631.     For example:
  2632.  
  2633.     If you have binary 1000 on the stack using the new word to set bit 1
  2634.     would leave you with binary 1010.
  2635.  
  2636. 4.  Now define a similar word which turns off a specified bit.
  2637.  
  2638. 5.  Define a word which puts on the stack a flag representing the status of
  2639.     a single specified bit from a 16-bit number n1 on the stack.
  2640.  
  2641.     The stack effect would be ( n1, Bit# - - - flag [1 or 0] ).
  2642.  
  2643.     Try using the result of this word as a flag to drive an IF...THEN
  2644.     statement in another word.
  2645.  
  2646. 6.  Define a word which toggles (or "flips") a specified bit from a 16-bit
  2647.     number on the stack.
  2648.  
  2649. 7.  Define a word which produces a number consisting of the "bit-difference"
  2650.     between two numbers on the stack.
  2651.  
  2652. 8.  Write a formatted output word to display a 32-bit signed integer as a
  2653.     string of digits, a decimal point, and 1 decimal digit.
  2654.  
  2655. 9.  Write a routine that, given the value of "x" on the stack, evaluates
  2656.     the following quadratic equation and returns a double-length result.
  2657.  
  2658.     7x² + 20x + 5
  2659.  
  2660. 10. In the previous problem, how large a value of "x" will work without
  2661.     overflowing 32 bits as a signed number?
  2662.  
  2663. 11. Write a word that prints the numbers 0 to 16 (decimal) in decimal,
  2664.     hexadecimal and binary form in three columns.
  2665.  
  2666.     For example:
  2667.  
  2668.     DECIMAL  0   HEX  0   BINARY  0
  2669.     DECIMAL  1   HEX  1   BINARY  1
  2670.     DECIMAL  2   HEX  2   BINARY  10
  2671.     .....
  2672.     DECIMAL  16  HEX  10  BINARY  10000
  2673.  
  2674. 12. Translate the following algebraic expression into a HeliOS definition:
  2675.  
  2676.         -  ab
  2677.          -----
  2678.            c
  2679.  
  2680.     given  ( a b c - - - ) as a stack parameter input.
  2681.  
  2682. 13. A histogram is a graphic representation of a series of values, where
  2683.     each value is shown by the height or length of a bar.
  2684.  
  2685.     Define a word called PLOT which will serve as a component to a general
  2686.     histogram application.
  2687.  
  2688.     Given a value between 0 and 100, PLOT should draw a horizontal row of
  2689.     stars on your display screen to represent the value.
  2690.  
  2691.     The catch?
  2692.  
  2693.     There are only 78 columns on the display screen!
  2694.  
  2695.     Thus a value of 100 must be plotted as 78 stars.
  2696.  
  2697.     50 must be plotted as 49 stars.
  2698.  
  2699.     0 must be plotted as 0 stars.
  2700.  
  2701.     etc.
  2702.  
  2703. 14. In command line "calculator" style convert the given temperatures, using
  2704.     the formulas below, and expressing all arguments and results in whole
  2705.     degrees:
  2706.  
  2707.     C  =  F - 32
  2708.           ------
  2709.            1.8
  2710.  
  2711.     F  =  (C x 1.8) + 32
  2712.  
  2713.     K  =  C + 273
  2714.  
  2715.  
  2716.     a.  0F   in Centigrade
  2717.     b.  212F in Centigrade
  2718.     c.  -32F in Centigrade
  2719.     d.  16C  in Fahrenheit
  2720.     e.  233K in Centigrade
  2721.  
  2722. 15. Now define actual HeliOS words to perform the temperature conversions.
  2723.  
  2724.     You could use names such as:
  2725.  
  2726.     F>C  F>K  C>F  C>K  K>F  K>C
  2727.  
  2728.     Test them with the values from the previous problem.
  2729.  
  2730. ************************************************************************
  2731. End
  2732. ************************************************************************
  2733.